'''
Created on Mar 15, 2014

@author: Eric
'''

import re

import lrparsing as lrp

from . import block
from . import domain

#######

class BadIndentationError(Exception):
    
    def __init__(self, line):
        super(BadIndentationError, self).__init__(line)
        self.line = line
    
    def __str__(self):
        return 'Line %s is wrongly indented' % self.line

#######

INDENT_RE = re.compile('[ \t]*')

def _preprocess_indent(text):
    stack = []
    indent = ['']
    accu = []
    for (i, line) in enumerate(text.split('\n')):
        if not stack:
            match = INDENT_RE.match(line).group()
            if (line != match) and (line[len(match)] != '#'):
                if match != indent[-1]:
                    if match.startswith(indent[-1]):
                        # we increased indentation
                        if accu:
                            accu[-1] += ' _BEGIN_'
                        indent.append(match)
                    else:
                        # we decreased indentation
                        while indent[-1].startswith(match) and (indent[-1] != match):
                            accu[-1] += ' _END_'
                            indent.pop()
                        if indent[-1] != match:
                            raise BadIndentationError(i + 1)
        for x in text:
            if x in '([{':
                stack.append(x)
            elif x in ')]}':
                rev = '([{'[')]}'.index(x)]
                assert stack[-1] == rev
                stack.pop()
        accu.append(line)
    if accu:
        while len(indent) > 1:
            accu[-1] += ' _END_'
            indent.pop()
    return '\n'.join(accu)

#######

class _Parser(lrp.Grammar):
    
    class T(lrp.TokenRegistry):
        integer        = lrp.Token(re = '[0-9]+')
        pascal_case_id = lrp.Token(re = '([A-Z][a-z0-9]*)+')
        camel_case_id  = lrp.Token(re = '[a-z][a-z0-9]*([A-Z][a-z0-9]*)+')
        lower_case_id  = lrp.Token(re = '[a-z][a-z0-9_]*')
        string         = lrp.Token(re = '"[^\\\\]*(\\\\[\\\\trn"][^\\\\]*)*"')
        char           = lrp.Token(re = "'[^\\\\]|(\\\\[\\\\trn'])'")
    
    integer_lit = T.integer * 1
    
    string_lit = T.string * 1
    
    char_lit = T.char * 1
    
    bool_lit = ( lrp.Keyword('true')
               | lrp.Keyword('false') )
    
    none_lit = lrp.Keyword('none')
    
    _expr = lrp.Ref('_expr')
    
    cell_expr = (_expr + '.') * (0, 1) + T.pascal_case_id
    
    fun_call_expr = ( ( _expr + '.' ) * (0, 1)
                    + T.pascal_case_id
                    + '('
                    + lrp.List(_expr, ',', 1)
                    + ')' )
    
    mem_call_expr = ( ( _expr + '.' ) * (0, 1)
                    + T.pascal_case_id
                    + '['
                    + lrp.List(_expr, ',')
                    + ']' )
    
    proc_call_expr = ( ( _expr + '.' ) * (0, 1)
                     + ( T.lower_case_id | T.camel_case_id )
                     + '('
                     + lrp.List(_expr, ',')
                     + ')' )

    init_constr_arg = T.pascal_case_id + '=' + _expr
    
    rebase_constr_arg = T.pascal_case_id + '~' + cell_expr
    
    _constr_arg = ( init_constr_arg
                  | rebase_constr_arg )
    
    constr_expr = ( ( _expr + '.' ) * (0, 1)
                  + T.pascal_case_id
                  + '('
                  + lrp.List(_constr_arg, ',', 1)
                  + ')' )
 
    fun_or_constr_expr = ( ( _expr + '.' ) * (0, 1)
                         + T.pascal_case_id
                         + '('
                         + ')' )
    
    var_expr = T.lower_case_id * 1
    
    unary_bool_expr = lrp.Keyword('not') + _expr
    
    bin_bool_1_expr = _expr << lrp.Keyword('and') << _expr
    
    bin_bool_2_expr = _expr << ( lrp.Keyword('or')
                               | lrp.Keyword('xor') ) << _expr
    
    compare_expr = _expr << lrp.Tokens('< <= > >= == !=') << _expr
    
    unary_arith_expr = '-' + _expr
    
    bin_arith_1_expr = _expr << lrp.Tokens('+ -') << _expr
    
    bin_arith_2_expr = _expr << lrp.Tokens('* / %') << _expr
    
    bin_arith_3_expr = _expr << lrp.Token('^') << _expr
    
    tuple_expr = '<' + lrp.List(_expr, ',') + '>'
    
    list_expr = '[' + lrp.List(_expr, ',') + ']'
    
    empty_map_or_set = ( lrp.Token('{') + lrp.Token('}') )
    
    key_value = ( _expr + ':' + _expr )
    
    map_expr = '{' + lrp.List(key_value, ',', 1) + '}'
    
    set_expr = '{' + lrp.List(_expr, ',', 1) + '}'
    
    _expr = lrp.Prio(integer_lit,
                     string_lit,
                     char_lit,
                     bool_lit,
                     none_lit,
                     fun_call_expr,
                     mem_call_expr,
                     proc_call_expr,
                     constr_expr,
                     cell_expr,
                     var_expr,
                     fun_or_constr_expr,
                     bin_arith_3_expr,
                     bin_arith_2_expr,
                     unary_arith_expr,
                     bin_arith_1_expr,
                     compare_expr,
                     unary_bool_expr,
                     bin_bool_1_expr,
                     bin_bool_2_expr,
                     tuple_expr,
                     list_expr,
                     map_expr,
                     set_expr,
                     empty_map_or_set)
    
    return_stmt = lrp.Keyword('return') + _expr

    assign_stmt = T.lower_case_id + '=' + _expr

    code = lrp.Ref('code')
    
    if_part = lrp.Keyword('if') + _expr + code
    
    elif_part = lrp.Keyword('elif') + _expr + code
    
    else_part = lrp.Keyword('else') + code
    
    if_stmt = if_part + elif_part * (0,) + else_part * (0, 1)
    
    page_decl = lrp.Ref('page_decl')
    
    import_decl = lrp.Ref('import_decl')
    
    while_stmt = ( lrp.Keyword('while')
                 + _expr
                 + code
                 + else_part * (0, 1) )
    
    do_until_stmt = ( lrp.Keyword('do') 
                    + code
                    + lrp.Keyword('until')
                    + _expr )
    
    case_part = ( lrp.Keyword('case')
                + _expr
                + code )
    
    switch_stmt = ( lrp.Keyword('switch')
                  + _expr
                  + case_part * (1,)
                  + else_part * (0, 1) )
    
    _statement = ( return_stmt
                 | assign_stmt
                 | if_stmt
                 | while_stmt
                 | do_until_stmt
                 | switch_stmt
                 | page_decl
                 | import_decl )

    code = '_BEGIN_' + _statement * (1,) + '_END_'
    
    simple_header = T.pascal_case_id * 1
    
    fun_header = T.pascal_case_id + '(' + lrp.List(T.lower_case_id, ',') + ')'
    
    mem_header = T.pascal_case_id + '[' + lrp.List(T.lower_case_id, ',') + ']'
    
    proc_header = T.camel_case_id + '(' + lrp.List(T.lower_case_id, ',') + ')'
    
    _member_header = ( simple_header
                     | fun_header
                     | mem_header
                     | proc_header )
    
    inline_member = _member_header + '=' + _expr
    
    code_member = _member_header + code
    
    module_path = ( lrp.Token('.') * (0,)
                  + lrp.List(T.lower_case_id, '.', 1) )

    import_decl = lrp.Keyword('import') + module_path

    _page_member = ( inline_member
                   | code_member
                   | import_decl )

    page_decl = ( lrp.Keyword('page')
                + T.pascal_case_id
                + ( '_BEGIN_'
                  + _page_member * (1,)
                  + '_END_' ) * (0, 1) )

    _decl = ( page_decl
            | import_decl )
    
    file = lrp.Repeat(_decl, min = 1)
    
    START = file
    
    COMMENTS = lrp.Token(re = '#[^\n]*')

#######

def _start(token):
    return Coordinates(token[3], token[4], token[2])

def _end(token):
    return _start(token).end(token[1])

def _tree_factory(match):
    if match[0] == _Parser.START:
        return match[1]
    
    elif match[0] == _Parser.file:
        return FileNode(list(match[1:]))
    
    elif match[0] == _Parser.page_decl:
        if len(match) == 3:
            return PageDeclNode(_start(match[1]),
                                match[2][1],
                                None,
                                _end(match[2]))
        else:
            return PageDeclNode(_start(match[1]),
                                match[2][1],
                                list(match[4:-1]),
                                None)
    
    elif match[0] == _Parser.module_path:
        return ModulePathNode(_start(match[1]),
                              ''.join([x[1] for x in match[1:]]),
                              _start(match[-1]))

    elif match[0] == _Parser.import_decl:
        return ImportDeclNode(_start(match[1]), match[2])
    
    elif match[0] == _Parser.simple_header:
        return SimpleHeaderNode(_start(match[1]), match[1][1], _end(match[1]))
    
    elif match[0] == _Parser.fun_header:
        return FunHeaderNode(_start(match[1]),
                             match[1][1],
                             [x[1] for x in match[3:-1:2]],
                             _end(match[-1]))
    
    elif match[0] == _Parser.mem_header:
        return MemHeaderNode(_start(match[1]),
                             match[1][1],
                             [x[1] for x in match[3:-1:2]],
                             _end(match[-1]))
    
    elif match[0] == _Parser.proc_header:
        return ProcHeaderNode(_start(match[1]),
                              match[1][1],
                              [x[1] for x in match[3:-1:2]],
                              _end(match[-1]))

    elif match[0] == _Parser.inline_member:
        return InlineMemberNode(match[1], match[3])
    
    elif match[0] == _Parser.code_member:
        return CodeMemberNode(match[1], match[2])
    
    elif match[0] == _Parser.code:
        return CodeNode(list(match[2:-1]))
    
    elif match[0] == _Parser.return_stmt:
        return ReturnStmtNode(_start(match[1]), match[2])
    
    elif match[0] == _Parser.assign_stmt:
        return AssignStmtNode(_start(match[1]), match[1][1], match[3])
    
    elif match[0] == _Parser.if_part:
        return CaseNode(_start(match[1]), match[2], match[3])
    
    elif match[0] == _Parser.elif_part:
        return CaseNode(_start(match[1]), match[2], match[3])
    
    elif match[0] == _Parser.else_part:
        return CaseNode(_start(match[1]), None, match[2])
    
    elif match[0] == _Parser.if_stmt:
        return IfStmtNode(list(match[1:]))

    elif match[0] == _Parser.while_stmt:
        if len(match) == 4:
            return WhileStmtNode(_start(match[1]),
                                 match[2],
                                 match[3],
                                 None)
        else:
            return WhileStmtNode(_start(match[1]),
                                 match[2],
                                 match[3],
                                 match[5].code)
    
    elif match[0] == _Parser.do_until_stmt:
        return DoUntilStmtNode(_start(match[1]), match[2], match[4])

    elif match[0] == _Parser.case_part:
        return CaseNode(_start(match[1]), match[2], match[3])
    
    elif match[0] == _Parser.switch_stmt:
        return SwitchStmtNode(list(match[1:]))
    
    elif match[0] in (_Parser.unary_arith_expr,
                      _Parser.unary_bool_expr):
        return UnaryOpExprNode(_start(match[1]), match[1][1], match[2])
    
    elif match[0] in (_Parser.bin_arith_3_expr,
                      _Parser.bin_arith_2_expr,
                      _Parser.bin_arith_1_expr,
                      _Parser.compare_expr,
                      _Parser.bin_bool_1_expr,
                      _Parser.bin_bool_2_expr):
        return BinaryOpExprNode(match[1], match[2][1], match[3])
    
    elif match[0] in (_Parser.integer_lit,
                      _Parser.string_lit):
        return ValueExprNode(_start(match[1]),
                             int(match[1][1]),
                             _end(match[1]))
    
    elif match[0] == _Parser.char_lit:
        return CharValueExprNode(_start(match[1]),
                                 eval(match[1][1]),
                                 _end(match[1]))
    
    elif match[0] == _Parser.bool_lit:
        return ValueExprNode(_start(match[1]),
                             match[1][1] == 'true',
                             _end(match[1]))
    
    elif match[0] == _Parser.none_lit:
        return ValueExprNode(_start(match[1]),
                             None,
                             _end(match[1]))

    elif match[0] == _Parser.tuple_expr:
        return TupleExprNode(_start(match[1]),
                             list(match[2:-1:2]),
                             _end(match[-1]))
    
    elif match[0] == _Parser.list_expr:
        return ListExprNode(_start(match[1]),
                            list(match[2:-1:2]),
                            _end(match[-1]))
    
    elif match[0] == _Parser.key_value:
        return KeyValueNode(None, match[1], match[3])

    elif match[0] == _Parser.map_expr:
        return MapExprNode(_start(match[1]),
                           list(match[2:-1:2]),
                           _end(match[-1]))
    
    elif match[0] == _Parser.set_expr:
        return SetExprNode(_start(match[1]),
                           list(match[2:-1:2]),
                           _end(match[-1]))
    
    elif match[0] == _Parser.empty_map_or_set:
        return ValueExprNode(_start(match[1]),
                             EMPTY_MAP_OR_SET,
                             _end(match[2]))
    
    elif match[0] == _Parser.cell_expr:
        if isinstance(match[1], ExpressionNode):
            return CellExprNode(None,
                                match[1],
                                match[3][1],
                                _end(match[3]))
        else:
            return CellExprNode(_start(match[1]),
                                None,
                                match[1][1],
                                _end(match[1]))
    
    elif match[0] == _Parser.var_expr:
        return VariableExprNode(_start(match[1]),
                                match[1][1],
                                _end(match[1]))
    
    elif match[0] == _Parser.fun_call_expr:
        if isinstance(match[1], ExpressionNode):
            return FunCallExprNode(None,
                                   match[1],
                                   match[3][1],
                                   list(match[5:-1:2]),
                                   _end(match[-1]))
        else:
            return FunCallExprNode(_start(match[1]),
                                   None,
                                   match[1][1],
                                   list(match[3:-1:2]),
                                   _end(match[-1]))
    
    elif match[0] == _Parser.mem_call_expr:
        if isinstance(match[1], ExpressionNode):
            return MemCallExprNode(None,
                                   match[1],
                                   match[3][1],
                                   list(match[5:-1:2]),
                                   _end(match[-1]))
        else:
            return MemCallExprNode(_start(match[1]),
                                   None,
                                   match[1][1],
                                   list(match[3:-1:2]),
                                   _end(match[-1]))
    
    elif match[0] == _Parser.proc_call_expr:
        if isinstance(match[1], ExpressionNode):
            return ProcCallExprNode(None,
                                    match[1],
                                    match[3][1],
                                    list(match[5:-1:2]),
                                    _end(match[-1]))
        else:
            return ProcCallExprNode(_start(match[1]),
                                    None,
                                    match[1][1],
                                    list(match[3:-1:2]),
                                    _end(match[-1]))
    
    elif match[0] == _Parser.init_constr_arg:
        return KeyValueNode(_start(match[1]),
                            match[1][1],
                            match[3])
    
    elif match[0] == _Parser.rebase_constr_arg:
        return KeyValueNode(_start(match[1]),
                            match[1][1],
                            RebaseExprNode(match[3]))
    
    elif match[0] == _Parser.constr_expr:
        if isinstance(match[1], ExpressionNode):
            return ConstructorExprNode(None,
                                       match[1],
                                       match[3][1],
                                       list(match[5:-1:2]),
                                       _end(match[-1]))
        else:
            return ConstructorExprNode(_start(match[1]),
                                       None,
                                       match[1][1],
                                       list(match[3:-1:2]),
                                       _end(match[-1]))
    
    elif match[0] == _Parser.fun_or_constr_expr:
        if isinstance(match[1], ExpressionNode):
            return FunOrConstrExprNode(None,
                                       match[1],
                                       match[3][1],
                                       _end(match[3]))
        else:
            return FunOrConstrExprNode(_start(match[1]),
                                       None,
                                       match[1][1],
                                       _end(match[1]))
    
    else:
        return match

#######

class Coordinates(object):
    
    def __init__(self, line, column, offset):
        self.line = line
        self.column = column
        self.offset = offset

    def end(self, text):
        line = self.line
        column = self.column
        offset = self.offset
        for x in text[:-1]:
            if x == '\n':
                line += 1
                column = 1
            else:
                column += 1
            offset += 1
        return Coordinates(line, column, offset)

    def text(self):
        return '(%s,%s)' % (self.line, self.column)

#######

class Node(object):
    
    def __init__(self):
        self.parent = None
    
    def start(self):
        if hasattr(self, '_start') and self._start:
            return self._start
        else:
            child = None
            for x in self.children():
                if x[1] is not None:
                    child = x[1]
                    break
            assert child
            if isinstance(child, list):
                return child[0].start()
            else:
                assert isinstance(child, Node)
                return child.start()

    def end(self):
        if hasattr(self, '_end') and self._end:
            return self._end
        else:
            child = None
            for x in reversed(self.children()):
                if x[1] is not None:
                    child = x[1]
                    break
            assert child
            if isinstance(child, list):
                return child[-1].end()
            else:
                assert isinstance(child, Node)
                return child.end()
    
    def location(self):
        return '%s-%s' % (self.start().text(), self.end().text())
    
    def children(self):
        return []

    def link_up(self):
        for (_, y) in self.children():
            if isinstance(y, list):
                for x in y:
                    x.parent = self
                    x.link_up()
            elif isinstance(y, Node):
                y.parent = self
                y.link_up()
    
    def bottom_up_walk(self, function):
        for (_, y) in self.children():
            if isinstance(y, list):
                for x in y:
                    x.bottom_up_walk(function)
            elif isinstance(y, Node):
                y.bottom_up_walk(function)
        function(self)

    def block(self):
        header = '%s @ %s-%s' % (self.__class__.__name__,
                                 self.start().text(),
                                 self.end().text())
        content = []
        for (x, y) in self.children():
            if isinstance(y, str):
                content.append('%s = [%s]' % (x, y))
            elif isinstance(y, list):
                assert y
                sub_block = block.VBlock([t.block() for t in y])
                content.append(block.VBlock([x, block.PBlock('| ', sub_block)]))
            elif y is None:
                content.append('%s = None' % x)
            else:
                assert isinstance(y, Node)
                content.append(block.VBlock([x, block.PBlock('| ', y.block())]))
        if content:
            return block.VBlock([header, block.PBlock('| ', block.VBlock(content))])
        else:
            return block.VBlock([header])

#######

class FileNode(Node):
    
    def __init__(self, decls):
        super(FileNode, self).__init__()
        self.decls = decls

    def children(self):
        return [('DECLS', self.decls)]

#######
    
class DeclarationNode(Node):
    pass

#######

class ModulePathNode(Node):
    
    def __init__(self, start, path, end):
        super(ModulePathNode, self).__init__()
        self._start = start
        self.path = path
        self._end = end
    
    def children(self):
        return [('PATH', self.path)]

#######

class ImportDeclNode(DeclarationNode):
    
    def __init__(self, start, path):
        super(ImportDeclNode, self).__init__()
        self._start = start
        self.path = path

    def children(self):
        return [('PATH' , self.path)]

#######

class PageDeclNode(DeclarationNode):
    
    def __init__(self, start, name, members, end):
        super(PageDeclNode, self).__init__()
        self._start = start
        self.name = name
        self.members = members
        self._end = end

    def children(self):
        return [('NAME'   , self.name),
                ('MEMBERS', self.members)]

#######

class PageMemberNode(Node):
    pass

#######

class SimpleHeaderNode(Node):
    
    def __init__(self, start, name, end):
        super(SimpleHeaderNode, self).__init__()
        self._start = start
        self.name = name
        self._end = end
    
    def children(self):
        return [('NAME', self.name)]

#######

class HeaderWithArgsNode(Node):
    
    def __init__(self, start, name, args, end):
        super(HeaderWithArgsNode, self).__init__()
        self._start = start
        self.name = name
        self.args = args
        self._end = end
    
    def children(self):
        return [('NAME', self.name),
                ('ARGS', self.args)]

#######

class FunHeaderNode(HeaderWithArgsNode):
    pass

class MemHeaderNode(HeaderWithArgsNode):
    pass

class ProcHeaderNode(HeaderWithArgsNode):
    pass

#######

class InlineMemberNode(Node):
    
    def __init__(self, header, expr):
        super(InlineMemberNode, self).__init__()
        self.header = header
        self.expr = expr
    
    def children(self):
        return [('HEADER', self.header),
                ('EXPR'  , self.expr)]

#######

class CodeMemberNode(Node):
    
    def __init__(self, header, code):
        super(CodeMemberNode, self).__init__()
        self.header = header
        self.code = code
        
    def children(self):
        return [('HEADER', self.header),
                ('CODE'  , self.code)]

#######

class StatementNode(Node):
    pass

#######

class CodeNode(Node):
    
    def __init__(self, stmts):
        self.stmts = stmts

    def children(self):
        return [('STMTS', self.stmts)]

#######

class ReturnStmtNode(StatementNode):
    
    def __init__(self, start, expr):
        super(ReturnStmtNode, self).__init__()
        self._start = start
        self.expr = expr
        
    def children(self):
        return [('EXPR', self.expr)]

#######

class AssignStmtNode(StatementNode):
    
    def __init__(self, start, name, expr):
        super(AssignStmtNode, self).__init__()
        self._start = start
        self.name = name
        self.expr = expr
    
    def children(self):
        return [('NAME', self.name),
                ('EXPR', self.expr)]

#######

class CaseNode(Node):
    
    def __init__(self, start, expr, code):
        super(CaseNode, self).__init__()
        self._start = start
        self.expr = expr
        self.code = code

    def children(self):
        return [('EXPR', self.expr),
                ('CODE', self.code)]

#######

class IfStmtNode(StatementNode):
    
    def __init__(self, cases):
        super(IfStmtNode, self).__init__()
        self.cases = cases
    
    def children(self):
        return [('CASES', self.cases)]

#######

class SwitchStmtNode(StatementNode):
    
    def __init__(self, cases):
        super(SwitchStmtNode, self).__init__()
        self.cases = cases
    
    def children(self):
        return [('CASES', self.cases)]

#######

class WhileStmtNode(StatementNode):
    
    def __init__(self, start, expr, main_code, else_code):
        super(WhileStmtNode, self).__init__()
        self._start = start
        self.expr = expr
        self.main_code = main_code
        self.else_code = else_code
    
    def children(self):
        return [('EXPR'     , self.expr),
                ('MAIN_CODE', self.main_code),
                ('ELSE_CODE', self.else_code)]

#######

class DoUntilStmtNode(StatementNode):
    
    def __init__(self, start, code, expr):
        super(DoUntilStmtNode, self).__init__()
        self._start = start
        self.code = code
        self.expr = expr

    def children(self):
        return [('CODE', self.code),
                ('EXPR', self.expr)]

#######

class ExpressionNode(Node):
    pass

#######

EMPTY_MAP_OR_SET = object()

#######

class ValueExprNode(ExpressionNode):
    
    def __init__(self, start, value, end):
        super(ValueExprNode, self).__init__()
        self._start = start
        self.value = value
        self._end = end
    
    def children(self):
        return [('VALUE', str(self.value))]

#######

class CharValueExprNode(ValueExprNode):
    pass

#######

class KeyValueNode(Node):
    
    def __init__(self, start, key, value):
        super(KeyValueNode, self).__init__()
        self._start = start
        self.key = key
        self.value = value
    
    def children(self):
        return [('KEY'  , self.key),
                ('VALUE', self.value)]

#######

class SeveralExprNode(ExpressionNode):
    
    def __init__(self, start, values, end):
        super(SeveralExprNode, self).__init__()
        self._start = start
        self.values = values
        self._end = end
    
    def children(self):
        return [('VALUES', self.values)]

#######

class TupleExprNode(SeveralExprNode):
    pass

class ListExprNode(SeveralExprNode):
    pass

class SetExprNode(SeveralExprNode):
    pass

class MapExprNode(SeveralExprNode):
    pass

#######

class ReferenceExprNode(ExpressionNode):
    
    def __init__(self, start, expr, name, end):
        super(ReferenceExprNode, self).__init__()
        self._start = start
        self.expr = expr
        self.name = name
        self._end = end
    
    def children(self):
        return [('EXPR', self.expr),
                ('NAME', self.name)]

#######

class CellExprNode(ReferenceExprNode):
    pass

class VariableExprNode(ReferenceExprNode):
    pass

class FunOrConstrExprNode(ReferenceExprNode):
    pass

#######

class RefWithArgsExprNode(ReferenceExprNode):
    
    def __init__(self, start, expr, name, args, end):
        super(RefWithArgsExprNode, self).__init__(start, expr, name, end)
        self.args = args
    
    def children(self):
        base = super(RefWithArgsExprNode, self).children()
        return base + [('ARGS', self.args)]

#######

class FunCallExprNode(RefWithArgsExprNode):
    pass

class MemCallExprNode(RefWithArgsExprNode):
    pass

class ProcCallExprNode(RefWithArgsExprNode):
    pass

class ConstructorExprNode(RefWithArgsExprNode):
    pass

#######

class RebaseExprNode(ExpressionNode):
    
    def __init__(self, expr):
        super(RebaseExprNode, self).__init__()
        self.expr = expr
    
    def children(self):
        return [('EXPR', self.expr)]

#######

class BinaryOpEnum(object):
    
    PLUS  = '+'
    MINUS = '-'
    MULT  = '*'
    DIV   = '/'
    MOD   = '%'
    EXP   = '^'
    LT    = '<'
    LE    = '<='
    GT    = '>'
    GE    = '>='
    EQ    = '=='
    NE    = '!='
    AND   = 'and'
    OR    = 'or'
    XOR   = 'xor'

class BinaryOpExprNode(ExpressionNode):
    
    def __init__(self, left, op, right):
        super(BinaryOpExprNode, self).__init__()
        self.left = left
        self.op = op
        self.right = right

    def children(self):
        return [('LEFT' , self.left),
                ('OP'   , self.op),
                ('RIGHT', self.right)]

#######

class UnaryOpEnum(object):
    
    MINUS = '-'
    NOT   = 'not'

class UnaryOpExprNode(ExpressionNode):
    
    def __init__(self, start, op, expr):
        self._start = start
        self.op = op
        self.expr = expr
    
    def children(self):
        return [('OP'  , self.op),
                ('EXPR', self.expr)]

#######

def parsed_domain():
    result = {}
    domain_ = domain.Domain()
    for (module_path, (_, file_path)) in domain_.catalog.items():
        handle = open(file_path)
        text = handle.read()
        handle.close()
        result[module_path] = parse(text)
    return result

def parse(text):
    result = _Parser.parse(_preprocess_indent(text),
                           tree_factory = _tree_factory)
    result.link_up()
    return result

#######

def check_grammar():
    lrp.compile_grammar(_Parser)
    unused = lrp.unused_rules(_Parser)
    if unused:
        print 'Unused rules: %s' % (unused,)
    print 'Grammar is OK'

#######

def run(path = None):
    if path:
        node = parsed_domain()[path]
        print node.block()
    else:
        for (path, node) in parsed_domain().items():
            print '=== %s ===' % path
            print node.block()

